#define _GNU_SOURCE
#include <limits.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/random.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/ether.h>
#include <netinet/ip_icmp.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>

int proc_pkt(void *msg, int rlen, char *ipaddr, int len)
{
	struct ethhdr *hdr = msg;
	char smac[32], dmac[32];
	printf("src_mac=%16s, dst_mac=%16s ", ether_ntoa_r((void *)hdr->h_source, smac), ether_ntoa_r((void *)hdr->h_dest, dmac));

	int af_family = 0;
	if (hdr->h_proto == htons(ETH_P_IP)) {
		af_family = AF_INET;
	} else if (hdr->h_proto == htons(ETH_P_IPV6)) {
		af_family = AF_INET6;
	}
	printf("ip=%s ", af_family == AF_INET ? "IPV4" : af_family == AF_INET6 ? "IPV6" : "UNKNOW");

	struct ip *ip = (void *)(hdr + 1);
	struct ip6_hdr *ip6 = (void *)ip;

	if (af_family == AF_INET) {
		inet_ntop(af_family, &ip->ip_src, ipaddr, len);
		printf("Src=%-16s ", ipaddr);
		inet_ntop(af_family, &ip->ip_dst, ipaddr, len);
		printf("dst=%-16s ", ipaddr);
	} else if (af_family == AF_INET6) {
		inet_ntop(af_family, &ip6->ip6_src, ipaddr, len);
		printf("Src=%-16s ", ipaddr);
		inet_ntop(af_family, &ip6->ip6_dst, ipaddr, len);
		printf("dst=%-16s ", ipaddr);
	}

	return 0;
}

int monitor_cdn(char *ip, int len)
{
	int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
	if (fd < 0) {
		perror("socket");
		exit(0);
	}

	struct timeval tv;
	tv.tv_sec = 120;
	tv.tv_usec = 0;
	setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

	struct sockaddr_ll addr;
	memset(&addr, 0, sizeof(addr));
	addr.sll_family = AF_PACKET;
	addr.sll_ifindex = if_nametoindex("enp4s0");
	addr.sll_protocol = htons(ETH_P_ALL);
	if (bind(fd, (void *)&addr, sizeof(addr)) < 0) {
		perror("bind");
		close(fd);
		return 0;
	}

	char *ptk_type[] = { "PACKET_HOST", "PACKET_BROADCAST", "PACKET_MULTICAST", "PACKET_OTHERHOST", "PACKET_OUTGOING" };
	char buf[2048];
	int rlen = 0;
	struct sockaddr_ll peer;
	socklen_t addrlen = sizeof(peer);
	while ((rlen = recvfrom(fd, buf, sizeof(buf), 0, (void *)&peer, &addrlen)) > 0) {
		// switch (peer.sll_pkttype) {
		// case PACKET_HOST:
		// case PACKET_BROADCAST:
		// case PACKET_MULTICAST:
		// case PACKET_OTHERHOST:
		// case PACKET_OUTGOING:
		// 	printf("%s ", ptk_type[peer.sll_pkttype]);
		// }
		proc_pkt(buf, rlen, ip, len);
		printf("%s\n\n", ptk_type[peer.sll_pkttype]);
		close(fd);
		return 0;
	}
	close(fd);
	return 0;
}

int main()
{
	char ip[INET6_ADDRSTRLEN];
	while (1) {
		monitor_cdn(ip, sizeof(ip));
		sleep(1);
	}
	return 0;
}